#include "Interface_LCD.h"
#include "Interface_LCD_Font.h"

// 1. 基本控制操作
// 1.1 初始化
void Interface_LCD_Init(void)
{
    Driver_FSMC_Init();

    Interface_LCD_Reset();

    Interface_LCD_BGOn();

    Interface_LCD_RegConfig();    //出厂调节，固定流程
}

// 1.1.1 复位
void Interface_LCD_Reset(void)
{
    // 直接将PG15拉低
    GPIOG->ODR &= ~GPIO_ODR_ODR15;
    // 稍作延迟，具体数据查数据手册p288
    Delay_us(10);   
    
    //然后拉高，再稍作延迟
    GPIOG->ODR |= GPIO_ODR_ODR15;
    Delay_ms(100);  //5~120ms
}

// 1.1.2 开/关背光
void Interface_LCD_BGOn(void)
{
    // PB0输出高电平，点亮背光LED
    GPIOB->ODR |= GPIO_ODR_ODR0;
}
void Interface_LCD_BGOff(void)
{
    // PB0输出低电平，关闭背光LED
    GPIOB->ODR &= ~GPIO_ODR_ODR0;
}

// 1.1.3 初始化Interface_LCD寄存器
void Interface_LCD_RegConfig(void)
{
    Interface_LCD_WriteCMD(0xE0);
    Interface_LCD_WriteData(0x00);
    Interface_LCD_WriteData(0x07);
    Interface_LCD_WriteData(0x10);
    Interface_LCD_WriteData(0x09);
    Interface_LCD_WriteData(0x17);
    Interface_LCD_WriteData(0x0B);
    Interface_LCD_WriteData(0x41);
    Interface_LCD_WriteData(0x89);
    Interface_LCD_WriteData(0x4B);
    Interface_LCD_WriteData(0x0A);
    Interface_LCD_WriteData(0x0C);
    Interface_LCD_WriteData(0x0E);
    Interface_LCD_WriteData(0x18);
    Interface_LCD_WriteData(0x1B);
    Interface_LCD_WriteData(0x0F);

    /* 2. 设置灰阶电压以调整TFT面板的伽马特性，负校准 */
    Interface_LCD_WriteData(0XE1);
    Interface_LCD_WriteData(0x00);
    Interface_LCD_WriteData(0x17);
    Interface_LCD_WriteData(0x1A);
    Interface_LCD_WriteData(0x04);
    Interface_LCD_WriteData(0x0E);
    Interface_LCD_WriteData(0x06);
    Interface_LCD_WriteData(0x2F);
    Interface_LCD_WriteData(0x45);
    Interface_LCD_WriteData(0x43);
    Interface_LCD_WriteData(0x02);
    Interface_LCD_WriteData(0x0A);
    Interface_LCD_WriteData(0x09);
    Interface_LCD_WriteData(0x32);
    Interface_LCD_WriteData(0x36);
    Interface_LCD_WriteData(0x0F);

    /* 3.  Adjust Control 3 (F7h)  */
    /*Interface_LCD_WriteCMD(0XF7);
   Interface_LCD_WriteData(0xA9);
   Interface_LCD_WriteData(0x51);
   Interface_LCD_WriteData(0x2C);
   Interface_LCD_WriteData(0x82);*/
    /* DSI write DCS command, use loose packet RGB 666 */

    /* 4. 电源控制1*/
    Interface_LCD_WriteCMD(0xC0);
    Interface_LCD_WriteData(0x11); /* 正伽马电压 */
    Interface_LCD_WriteData(0x09); /* 负伽马电压 */

    /* 5. 电源控制2 */
    Interface_LCD_WriteCMD(0xC1);
    Interface_LCD_WriteData(0x02);
    Interface_LCD_WriteData(0x03);

    /* 6. VCOM控制 */
    Interface_LCD_WriteCMD(0XC5);
    Interface_LCD_WriteData(0x00);
    Interface_LCD_WriteData(0x0A);
    Interface_LCD_WriteData(0x80);

    /* 7. Frame Rate Control (In Normal Mode/Full Colors) (B1h) */
    Interface_LCD_WriteCMD(0xB1);
    Interface_LCD_WriteData(0xB0);
    Interface_LCD_WriteData(0x11);

    /* 8.  Display Inversion Control (B4h) （正负电压反转，减少电磁干扰）*/
    Interface_LCD_WriteCMD(0xB4);
    Interface_LCD_WriteData(0x02);

    /* 9.  Display Function Control (B6h)  */
    Interface_LCD_WriteCMD(0xB6);
    Interface_LCD_WriteData(0x0A);
    Interface_LCD_WriteData(0xA2);

    /* 10. Entry Mode Set (B7h)  */
    Interface_LCD_WriteCMD(0xB7);
    Interface_LCD_WriteData(0xc6);

    /* 11. HS Lanes Control (BEh) */
    Interface_LCD_WriteCMD(0xBE);
    Interface_LCD_WriteData(0x00);
    Interface_LCD_WriteData(0x04);

    /* 12.  Interface Pixel Format (3Ah) */
    Interface_LCD_WriteCMD(0x3A);
    Interface_LCD_WriteData(0x55); /* 0x55 : 16 bits/pixel  */

    /* 13. Sleep Out (11h) 关闭休眠模式 */
    Interface_LCD_WriteCMD(0x11);

    /* 14. 设置屏幕方向和RGB */
    Interface_LCD_WriteCMD(0x36);
    Interface_LCD_WriteData(0x08);

    Delay_ms(120);

    /* 14. display on */
    Interface_LCD_WriteCMD(0x29);
}





/*----------------------------------------------------------------------------------*/
// 2. 具体的命令操作（我每实现一个具体的功能，都需要线规划好绘画区域，和发送写命令和写数据）

// 2.1 写命令（发出一个指令）
void Interface_LCD_WriteCMD(uint16_t cmd)
{
    *LCD_ADDR_CMD = cmd;
}

// 2.2 写数据（发出一个数据）
void Interface_LCD_WriteData(uint16_t data)
{
    *LCD_ADDR_DATA = data;
}

// 2.3 读数据
uint16_t Interface_LCD_ReadData(void)
{
    return *LCD_ADDR_DATA;
}


// 2.4 设置绘画区域范围，给定起始点的坐标（行列号），以及区域的宽和高
void Interface_LCD_SetArea(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
{
    //1.设置列的范围
    //发送命令
    Interface_LCD_WriteCMD(0x2a);   //0x2a：Column Address Set为设置范围的列的命令；   
    //发送数据
    //根据Interface_LCD驱动芯片的数据手册的命令格式要求如下： 
    //先发送列的起始位置（分两次发，先发送高位，再发送低位）
    Interface_LCD_WriteData(x >> 8);
    Interface_LCD_WriteData(x & 0xFF);
    //再发送列的结束位置（分两次发，先发送高位，再发送低位）
    Interface_LCD_WriteData((x + w - 1) >> 8);
    Interface_LCD_WriteData((x + w - 1) & 0xFF);
    
    
    //2.设置行的范围
    //发送命令
    Interface_LCD_WriteCMD(0x2b); //0x2b：Page Address Set为设置范围的行的命令；
    //发送数据
    //根据Interface_LCD驱动芯片的数据手册的命令格式要求如下： 
    //先发送行的起始位置（分两次发，先发送高位，再发送低位）
    Interface_LCD_WriteData(y >> 8);
    Interface_LCD_WriteData(y & 0xFF);
    //再发送行的结束位置（分两次发，先发送高位，再发送低位）
    Interface_LCD_WriteData((y + h - 1) >> 8);
    Interface_LCD_WriteData((y + h - 1) & 0xFF);
}






/*----------------------------------------------------------------------------------*/
//3. 各种功能实现

//3.1 打印Interface_LCD的ID信息
void Interface_LCD_ReadId(void)
{
  // 1. 发送命令
  Interface_LCD_WriteCMD(0x04);
  // 2. 接受4次数据
  Interface_LCD_ReadData(); // 第一次数据没有意义
  uint16_t id1 = Interface_LCD_ReadData();
  uint16_t id2 = Interface_LCD_ReadData();
  uint16_t id3 = Interface_LCD_ReadData();
  // 3. 打印输出看效果
  printf("id1 = %d \n", id1);
  printf("id2 = %d \n", id2);
  printf("id3 = %d \n", id3);
}

//3.2 清屏（设置全屏背景颜色）   
void Interface_LCD_ClearSreen(uint16_t color)
{
   
    //注意：Interface_LCD默认工作模式是行扫描--->If Memory Access Control (36h) B5 = 0:
    
    //设置要绘画的区域范围
    Interface_LCD_SetArea(0, 0, LCD_W, LCD_H);
    
    //发送命令
    Interface_LCD_WriteCMD(0x2c);
       
    //发送要写的数据; 数据--->每一个像素点的颜色 16位
    for(uint32_t i=0; i<LCD_W * LCD_H; i++)
    {
        //默认行扫描，会自动换行       
        Interface_LCD_WriteData(color);
    }
      
}

//3.3 画单个ASCII字符方法
//参数：x--->要显示的位置的x坐标，y--->要显示的位置的y坐标，height--->要显示的字符的字体高度，c--->要显示的字符，fColor--->要显示的字符的字体颜色，bColor--->要显示的字符的背景颜色
void Interface_LCD_WriteASCIIChar(uint16_t x, uint16_t y, uint8_t height, uint8_t c, uint16_t fColor, uint16_t bColor)
{
    //1.确定绘画的区域
    //注意：ASCII的显示宽度和高度的比例是1:2
    Interface_LCD_SetArea(x, y, height / 2, height);
    
    // 2.发送写命令
    Interface_LCD_WriteCMD(0x2c);
    
    //找到该字符对应ASCII字库/数组的下标
    //因为字库是从空格开始，表示下标0的
    uint16_t index = c - ' '; 
    
    //2.1 发送数据
    
    //先定位到字库的哪一种字体数组，通过字体的高度即可区分出来
    //因为高度为12和16的字符的宽，都只需要1字节空间即可存储，所以直接解析数组里的每一字节数据即可
    if (height == 12 || height == 16)
    {
        // 外层遍历，相当于遍历得到某行整个字节数据 
        for (uint8_t i = 0; i < height; i++)
        {
            // 找到数组中某个字节数据
            uint8_t tmpByte = (height == 12 ? ascii_1206[index][i] : ascii_1608[index][i]);
            
            // 内层遍历，遍历某个字节数据的某一位
            for (uint8_t j = 0; j < height / 2; j++)
            {
                // 取字模的数据顺序是低位先行--->即0x01实际上是0x80，这个时候就得从最低位开始读
                if (tmpByte & 0x01)
                {
                    //正片显示，1表示当前像素点显示字体颜色
                    Interface_LCD_WriteData(fColor);
                }
                else
                {
                    //正片显示，0表示当前像素点显示背景颜色
                    Interface_LCD_WriteData(bColor);
                }
                tmpByte >>= 1;
            }
        }
    }
    //因为高度为24和32的字符的宽，需要2字节空间才可存储，所以要额外处理解析。如12宽的奇数读8位，偶数读4位
    else if (height == 24 || height == 32)
   { 
        // 外层遍历，相当于遍历得到某行整个字节数据
        //这里乘2是因为，字符的每一行由有16位表达
        for (uint8_t i = 0; i < height * 2; i++)
        {
            // 找到数组中某个字节数据
            uint8_t tmpByte = height == 24 ? ascii_2412[index][i] : ascii_3216[index][i];
    
            //判断要从某个字节中取多少位
            uint8_t jCount = 8;
            //如果高度是24，且由于低位先行，所以偶数取8位，奇数取4位
                if (height == 24 && (i % 2) == 1)
            {
                jCount = 4;
            }
    
            // 内层遍历，遍历某个字节数据的某一位
            for (uint8_t j = 0; j < jCount; j++)
            {
                // 低位先行
                if (tmpByte & 0x01)
                {
                    Interface_LCD_WriteData(fColor);
                }
                else
                {
                    Interface_LCD_WriteData(bColor);
                }
                tmpByte >>= 1;
            }
        }
   }
}

// 写ASCII字符串方法
void Interface_LCD_WriteASCIIString(uint16_t x, uint16_t y, uint8_t height, uint8_t *str, uint16_t size, uint16_t fColor, uint16_t bColor)
{
  // 1. 遍历提取每个字符
  for (uint16_t i = 0; i < size; i++)
  {
    // 特殊情况：如果超过屏幕宽度，换行写
    if (str[i] == '\n')
    {
      y += height;
      x = 0;
      continue;
    }
    if (x + height / 2 > LCD_W)
    {
      y += height;
      x = 0;
    }
    // 2. 写入单个字符数据
    Interface_LCD_WriteASCIIChar(x, y, height, str[i], fColor, bColor);
    x += height / 2;
  }
}

// 写汉字方法
void Interface_LCD_WriteChineseChar(uint16_t x, uint16_t y, uint8_t height, uint8_t index, uint16_t fColor, uint16_t bColor)
{
  // 1. 设置绘画区域(显示区域)
  Interface_LCD_SetArea(x, y, height, height);
  // 2. 发送数据
  // 2.1. 发送写入命令
  Interface_LCD_WriteCMD(0x2c);
  // 2.2. 写入数据

  // 外层遍历，遍历出所有字节
  for (uint8_t i = 0; i < height * 4; i++)
  {
    // 找到数组中某个字节数据
    uint8_t tmpByte = chinese[index][i];

    // 内层遍历，遍历某个字节数据的某一位
    for (uint8_t j = 0; j < 8; j++)
    {
      // 低位先行
      if (tmpByte & 0x01)
      {
        Interface_LCD_WriteData(fColor);
      }
      else
      {
        Interface_LCD_WriteData(bColor);
      }
      tmpByte >>= 1;
    }
  }
}

// 画图方法
void Interface_LCD_DrawImage(uint16_t x, uint16_t y)
{
  // 1. 设置绘画区域(显示区域)
  Interface_LCD_SetArea(x, y, 206, 54);
  // 2. 发送数据
  // 2.1. 发送写入命令
  Interface_LCD_WriteCMD(0x2c);
  // 2.2. 写入数据
  for (uint16_t i = 0; i < 206 * 54 * 2; i += 2)
  {
    //uint16_t p = (uint16_t)(gImage_logo[i] | (gImage_logo[i + 1] << 8));
    //Interface_LCD_WriteData(p);
  }
}

// 画点方法
void Interface_LCD_DrawPoint(uint16_t x, uint16_t y, uint16_t w, uint16_t color)
{
  // 1. 设置绘画区域(显示区域)
  Interface_LCD_SetArea(x, y, w, w);
  // 2. 发送数据
  // 2.1. 发送写入命令
  Interface_LCD_WriteCMD(0x2c);
  // 2.2. 发送数据
  for (uint16_t i = 0; i < w * w; i++)
  {
    Interface_LCD_WriteData(color);
  }
}

// 画线方法
void Interface_LCD_DrawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t w, uint16_t color)
{
  /*
    数学公式：
      y = k * x + b
      k = (y2 - y1) / (x2 - x1)
        => b = y1 - k * x1
  */

  if (x1 == x2)
  {
    for (uint16_t y = y1; y <= y2; y++)
    {
      Interface_LCD_DrawPoint(x1, y, w, color);
    }

    return;
  }

  double k = (y2 - y1) / (x2 - x1);
  double b = y1 - k * x1;

  for (uint16_t x = x1; x <= x2; x++)
  {
    Interface_LCD_DrawPoint(x, k * x + b, w, color);
  }
}

// 画矩形方法
void Interface_LCD_DrawRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t line_w, uint16_t color)
{
  uint16_t x1 = x;
  uint16_t y1 = y;
  uint16_t x2 = x + w - 1;
  uint16_t y2 = y + h - 1;
  // 画4条线段
  Interface_LCD_DrawLine(x1, y1, x2, y1, line_w, color);
  Interface_LCD_DrawLine(x1, y1, x1, y2, line_w, color);
  Interface_LCD_DrawLine(x2, y1, x2, y2, line_w, color);
  Interface_LCD_DrawLine(x1, y2, x2, y2, line_w, color);
}

// 画空心圆
void Interface_LCD_DrawCircle(uint16_t x, uint16_t y, uint16_t r, uint16_t w, uint16_t color)
{
  /*
    数学公式：
      x1 = sin(弧度) * r + x
      y1 = cos(弧度) * r + y

      弧度 / π = 角度 / 180 => 弧度 = 角度 * π / 180
  */

  for (uint16_t theta = 0; theta < 360; theta++)
  {
    uint16_t x1 = sin(theta * 3.14 / 180) * r + x;
    uint16_t y1 = cos(theta * 3.14 / 180) * r + y;

    Interface_LCD_DrawPoint(x1, y1, w, color);
  }
}

// 画空心圆(优化)
void Interface_LCD_DrawCircle_Pro(uint16_t x, uint16_t y, uint16_t r, uint16_t w, uint16_t color)
{
  /*
    数学公式：
      x1 = sin(弧度) * r + x
      y1 = cos(弧度) * r + y

      弧度 / π = 角度 / 180 => 弧度 = 角度 * π / 180
  */

  for (uint16_t theta = 0; theta < 90; theta++)
  {
    uint16_t delta_x = sin(theta * 3.14 / 180) * r;
    uint16_t delta_y = cos(theta * 3.14 / 180) * r;

    // 第一象限
    uint16_t x1 = x + delta_x;
    uint16_t y1 = y + delta_y;
    Interface_LCD_DrawPoint(x1, y1, w, color);

    // 第二象限
    x1 = x + delta_x;
    y1 = y - delta_y;
    Interface_LCD_DrawPoint(x1, y1, w, color);

    // 第三象限
    x1 = x - delta_x;
    y1 = y - delta_y;
    Interface_LCD_DrawPoint(x1, y1, w, color);

    // 第四象限
    x1 = x - delta_x;
    y1 = y + delta_y;
    Interface_LCD_DrawPoint(x1, y1, w, color);
  }
}

// 画实心圆
void Interface_LCD_DrawFilledCircle(uint16_t x, uint16_t y, uint16_t r, uint16_t w, uint16_t fColor, uint16_t bColor)
{
  for (uint16_t i = 0; i <= r; i++)
  {

    for (uint16_t theta = 0; theta < 360; theta++)
    {
      uint16_t x1 = sin(theta * 3.14 / 180) * i + x;
      uint16_t y1 = cos(theta * 3.14 / 180) * i + y;

      if (i == r)
      {
        Interface_LCD_DrawPoint(x1, y1, w, fColor);
      }
      else
      {
        Interface_LCD_DrawPoint(x1, y1, 2, bColor);
      }
    }
  }
}

// 画实心圆（优化 ）
void Interface_LCD_DrawFilledCircle_Pro(uint16_t x, uint16_t y, uint16_t r, uint16_t w, uint16_t fColor, uint16_t bColor)
{
  for (uint16_t theta = 0; theta < 90; theta++)
  {
    uint16_t delta_x = sin(theta * 3.14 / 180) * r;
    uint16_t delta_y = cos(theta * 3.14 / 180) * r;

    // 画线
    uint16_t x1 = x - delta_x;
    uint16_t y1 = y + delta_y;
    uint16_t x2 = x + delta_x;
    uint16_t y2 = y + delta_y;

    Interface_LCD_DrawLine(x1 + w, y1, x2 - w, y2, w, bColor);

    // 画两个点
    Interface_LCD_DrawPoint(x1, y1, w, fColor);
    Interface_LCD_DrawPoint(x2, y2, w, fColor);

    // 画线
    x1 = x - delta_x;
    y1 = y - delta_y;
    x2 = x + delta_x;
    y2 = y - delta_y;

    Interface_LCD_DrawLine(x1 + w, y1, x2 - w, y2, w, bColor);

    // 画两个点
    Interface_LCD_DrawPoint(x1, y1, w, fColor);
    Interface_LCD_DrawPoint(x2, y2, w, fColor);
  }
}

